package com.wn.icloud.plugin.generator;

import com.google.common.collect.Maps;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.StringBuilderWriter;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.regex.Matcher;

/**
 * @author kyy
 * @date 2019/04/13
 */
@Slf4j
public class CodeGenerator extends BaseCodeGenerator {

    private Configuration cfg;

    /**
     * @param mavenModuleName maven 模块名称
     * @param basePackage     基础包，格式为 com.xxx 形式
     * @param moduleName      模块名称，格式为 xxx.yyy 形式
     */
    public CodeGenerator(String mavenModuleName, String basePackage, String moduleName) {

        super(mavenModuleName, basePackage, moduleName);

        cfg = new Configuration(Configuration.VERSION_2_3_28);
        cfg.setClassForTemplateLoading(CodeGenerator.class, "/ftl");
        cfg.setDefaultEncoding("UTF-8");
    }

    public void storeFile() {
        String baseCodePath = "src" + File.separator +
                "main" + File.separator +
                "java" + File.separator +
                basePackagePath + File.separator +
                modulePath + File.separator;
        copyDir(baseCodePath, "service");
        //  拷贝实体
//        if (getNoOverWriteDomain() != null && getNoOverWriteDomain().isEmpty() == false) {
//            copyDir(baseCodePath, "domain");
//        }
    }

    private void copyDir(String baseCodePath, String dirName) {
        String distDir = baseCodePath + dirName;
        File file = new File(distDir);
        if (!file.exists()) {
            return;
        }
        try {
            FileUtils.copyDirectory(new File(distDir), new File(distDir + "_"));
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    public void restoreFile() {

        String baseCodePath = "src" + File.separator +
                "main" + File.separator +
                "java" + File.separator +
                basePackagePath + File.separator +
                modulePath + File.separator;
        try {
            // 删除生成的 repository
            String repoDir = baseCodePath + "repository";
            FileUtils.deleteDirectory(new File(repoDir));

            // 删除生成的 controller
            String restDir = baseCodePath + "web" + File.separator + "rest";
            FileUtils.deleteDirectory(new File(restDir));

            // 删除新生成 remote.impl
            String serviceImplDir = baseCodePath + "service" + File.separator + "impl";
            FileUtils.deleteDirectory(new File(serviceImplDir));

            // 删除新生成的service
            File newServices = new File(baseCodePath + "service");
            if (newServices.exists()) {
                FileUtils.listFiles(newServices, new String[]{"java"}, false)
                        .forEach(File::delete);
            }

            // 删除 liquibase
            String dbChangelogPath = "src" + File.separator +
                    "main" + File.separator +
                    "resources" + File.separator +
                    "config" + File.separator +
                    "liquibase" + File.separator +
                    "changelog" + File.separator;
            FileUtils.deleteDirectory(new File(dbChangelogPath));

            // 删除单元测试
            String unitTestPath = "src" + File.separator +
                    "test" + File.separator +
                    "java" + File.separator +
                    basePackagePath + File.separator +
                    modulePath + File.separator +
                    "web" + File.separator +
                    "rest" + File.separator;
            File unitFile = new File(unitTestPath);
            if (unitFile.exists()) {
                FileUtils.deleteDirectory(unitFile);
            }
            // 修改domain基类
            File newDomain = new File(baseCodePath + "domain");
            if (newDomain.exists()) {
                Collection<File> files = FileUtils.listFiles(newDomain, new String[]{"java"}, false);
                files.forEach(this::changeDomainBaseClass);
            }

            // 修改dto基类
            File newDto = new File(baseCodePath + "service/dto");
            if (newDto.exists()) {
                Collection<File> files = FileUtils.listFiles(newDto, new String[]{"java"}, false);
                files.forEach(this::changeDtoBaseClass);
            }

            String cacheServiceDirPath = baseCodePath + "service_";
            File cacheServiceDir = new File(cacheServiceDirPath);
            if (cacheServiceDir.exists()) {
                //拷贝老的serviceImpl
                File serviceImpl = new File(cacheServiceDir + File.separator + "impl");
                if (serviceImpl.exists()) {
                    FileUtils.copyDirectory(serviceImpl,
                            new File(serviceImplDir));
                }
                //拷贝老的service文件
                FileUtils.listFiles(cacheServiceDir, new String[]{"java"}, false)
                        .forEach(file -> {
                            try {
                                FileUtils.copyFileToDirectory(file,
                                        new File(baseCodePath + "service"));
                            } catch (IOException e) {
                                log.error(e.getMessage(), e);
                            }
                        });
                // 删除缓存的service
                FileUtils.deleteDirectory(cacheServiceDir);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 生产代码
     *
     * @param type   类型 dao、service、serviceImpl、remote、
     * @param domain
     * @throws IOException
     * @throws TemplateException
     */
    public void genCode(String type, String domain) throws IOException, TemplateException {

        log.info("gen code for domain:{} type:{} ", domain, type);

        domain = StringUtils.uncapitalize(domain);
        String Domain = StringUtils.capitalize(domain);


        HashMap<String, Object> map = Maps.newHashMap();
        map.put("domain", domain);
        map.put("basePackage", basePackage);
        map.put("moduleName", moduleName);
        map.put("Domain", Domain);

        String relativePathName = getCodeFileRelativePathName(type, domain, basePackagePath, modulePath);
        genCodeFormTemplate(relativePathName, getTemplateName(type), map);
    }

    /**
     * 获取源码的相对路径
     *
     * @param domain
     * @param basePackagePath
     * @param modulePath
     * @return
     */
    protected String getCodeFileRelativePathName(String type, String domain, String basePackagePath, String modulePath) {
        String Domain = StringUtils.capitalize(domain);
        String Type = StringUtils.capitalize(type);
        if (StringUtils.equals("service.impl", type)) {
            Type = "ServiceImpl";
            type = RegExUtils.replaceAll(type, "\\.", Matcher.quoteReplacement(File.separator));
        }
        String[] ps = {
                "src",
                "main",
                "java",
                basePackagePath,
                modulePath,
                type,
                Domain + Type + ".java"};
        return StringUtils.join(ps, File.separator);
    }

    /**
     * 获取模板名称
     *
     * @return
     */
    protected String getTemplateName(String type) {
        return type;
    }

    private void genCodeFormTemplate(String distCodeFilePathName, String templateName, Object params) throws IOException, TemplateException {
        File file = new File(distCodeFilePathName);
        if (file.exists()) {
            if (isOverwrite() == false) {
                log.info("skip file:" + distCodeFilePathName);
                return;
            }
            log.info("overwrite file:" + distCodeFilePathName);
        } else {
            log.info("gen file:" + distCodeFilePathName);
        }
        Template temp = cfg.getTemplate(templateName + ".ftl");
        Writer out = new StringBuilderWriter(new StringBuilder());
        temp.process(params, out);
        String code = out.toString();
        FileUtils.writeStringToFile(file, code, "utf-8");
    }

    private void changeDomainBaseClass(File file) {
        log.info("changeDomainBaseClass:" + file.getPath());
        try {
            String content = FileUtils.readFileToString(file, "UTF-8");
            content = content.replaceAll("implements\\sSerializable", "extends BaseEntityDomain");
            String newLine = "import " + basePackage + ".domain.BaseEntityDomain;";
            if (!StringUtils.contains(content, newLine)) {
                content = addBeforeImport(content, newLine);
            }
            FileUtils.writeStringToFile(file, content, "UTF-8");
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    private void changeDtoBaseClass(File file) {
        try {
            String content = FileUtils.readFileToString(file, "UTF-8");
            if (StringUtils.contains(content, "extends BaseAuditingDTO")) {
                return;
            }
            log.info("changeDtoBaseClass:" + file.getPath());
            content = content.replaceAll("implements\\sSerializable", "extends BaseAuditingDTO");
            String newLine = "import " + basePackage + ".dto.BaseAuditingDTO;";
            if (!StringUtils.contains(content, newLine)) {
                content = addBeforeImport(content, newLine);
            }
            FileUtils.writeStringToFile(file, content, "UTF-8");
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    private static String addBeforeImport(String content, String line) {
        int anImport = content.indexOf("import");
        content = StringUtils.join(
                content.substring(0, anImport),
                line,
                "\n",
                content.substring(anImport));
        return content;
    }

    public static void main(String[] args) throws IOException, TemplateException {

        CodeGenerator codeGenerator = new CodeGenerator("xxx", "com.test", "m1");
        codeGenerator.genCode("dao", "User");
        codeGenerator.genCode("service", "User");
        codeGenerator.genCode("service.impl", "User");
        codeGenerator.genCode("controller", "User");

    }

}
